/*
 * (C) 2007-2010 Taobao Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 *
 *
 * Version: 0.1: DB_client.cpp,v 0.1 2011/08/27 17:38:52 chuanhui Exp $
 *
 * Authors:
 *   chuanhui <rizhao.ych@taobao.com>
 *     - some work details if you want
 *
 */
#include "DB.h"  // As an example

#include <protocol/TBinaryProtocol.h>
#include <protocol/TJSONProtocol.h>
#include <transport/THttpClient.h>
#include <transport/TTransportUtils.h>
#include <transport/TSocket.h>
#include <transport/TSSLSocket.h>
#include <async/TEvhttpClientChannel.h>
#include <server/TNonblockingServer.h> // <event.h>

#include <boost/shared_ptr.hpp>
#include <boost/program_options.hpp>

#include <iostream>

using namespace boost;
using namespace std;
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace apache::thrift::async;

using namespace cloudbase;

// Current time, microseconds since the epoch
uint64_t now()
{
  int64_t ret;
  struct timeval tv;

  gettimeofday(&tv, NULL);
  ret = tv.tv_sec;
  ret = ret*1000*1000 + tv.tv_usec;
  return ret;
}

int main(int argc, char **argv) {
  string host = "localhost";
  int port = 9090;
  int recvTimeout = 500; // 500ms

  int numTests = 1;

  string transport_type = "framed";
  string protocol_type = "binary";

  program_options::options_description desc("Allowed options");
  desc.add_options()
      ("help,h", "produce help message")
      ("host", program_options::value<string>(&host)->default_value(host), "Host to connect")
      ("port", program_options::value<int>(&port)->default_value(port), "Port number to connect")
      ("transport", program_options::value<string>(&transport_type)->default_value(transport_type), "Transport: buffered, framed, http")
      ("protocol", program_options::value<string>(&protocol_type)->default_value(protocol_type), "Protocol: binary, json")
      ("testloops,n", program_options::value<int>(&numTests)->default_value(numTests), "Number of Tests")
      ("recvtime", program_options::value<int>(&recvTimeout)->default_value(recvTimeout), "Receive timeout")
  ;

  program_options::variables_map vm;
  program_options::store(program_options::parse_command_line(argc, argv, desc), vm);
  program_options::notify(vm);    

  if (vm.count("help")) {
    cout << desc << "\n";
    return 1;
  }

  try {   
    if (!protocol_type.empty()) {
      if (protocol_type == "binary") {
      } else if (protocol_type == "json") {
      } else {
          throw invalid_argument("Unknown protocol type "+protocol_type);
      }
    }

	if (!transport_type.empty()) {
      if (transport_type == "buffered") {
      } else if (transport_type == "framed") {
      } else if (transport_type == "http") {
      } else {
          throw invalid_argument("Unknown transport type "+transport_type);
      }
    }

  } catch (std::exception& e) {
    cerr << e.what() << endl;
    cout << desc << "\n";
    return 1;
  }

  shared_ptr<TTransport> transport;
  shared_ptr<TProtocol> protocol;

  shared_ptr<TSocket> socket;
  shared_ptr<TSSLSocketFactory> factory;

  socket = shared_ptr<TSocket>(new TSocket(host, port));
  socket->setRecvTimeout(recvTimeout); // set recv timeout

  if (transport_type.compare("http") == 0) {
    shared_ptr<TTransport> httpSocket(new THttpClient(socket, host, "/service"));
    transport = httpSocket;
  } else if (transport_type.compare("framed") == 0){
    shared_ptr<TFramedTransport> framedSocket(new TFramedTransport(socket));
    transport = framedSocket;
  } else{
    shared_ptr<TBufferedTransport> bufferedSocket(new TBufferedTransport(socket));
    transport = bufferedSocket;
  }

  if (protocol_type.compare("json") == 0) {
    shared_ptr<TProtocol> jsonProtocol(new TJSONProtocol(transport));
    protocol = jsonProtocol;
  } else{
    shared_ptr<TBinaryProtocol> binaryProtocol(new TBinaryProtocol(transport));
    protocol = binaryProtocol;
  }

  // Connection info
  cout << "Connecting (" << transport_type << "/" << protocol_type << ") to: " << port;
  cout << endl;


  DBClient client(protocol);

  uint64_t start = now();

  try {
    transport->open();
  } catch (TTransportException& ttx) {
    printf("Connect failed: %s\n", ttx.what());
    return 1;
  }

  char key_buf[128];
  char value_buf[128];
  for (int test = 0; test < numTests; ++test) {
    sprintf(key_buf, "key%08d", test);
    sprintf(value_buf, "value%08d", test);
    string key(key_buf);
    string value(value_buf);
    client.Put(key, value);
  }
  transport->close();

  printf("Totol put time: %lu us\n", now() - start);

  start = now();

  try {
    transport->open();
  } catch (TTransportException& ttx) {
    printf("Connect failed: %s\n", ttx.what());
    return 1;
  }

  for (int test = 0; test < numTests; ++test) {
    sprintf(key_buf, "key%08d", test);
    string key(key_buf);
    std::string value;
    client.Get(value, key);
    //cout << value << endl;
  }

  transport->close();
  printf("Total get time: %lu us\n", now()-start);

  start = now(); 
  try {
    transport->open();
  } catch (TTransportException& ttx) {
    printf("Connect failed: %s\n", ttx.what());
    return 1;
  }
  for (int test = 0; test < numTests; ++test) {
    sprintf(key_buf, "key%08d", test);
    string key(key_buf);
    client.Delete(key);
  }
  transport->close();
  printf("Total delete time: %lu us\n", now() - start);

  start = now(); 
  try {
    transport->open();
  } catch (TTransportException& ttx) {
    printf("Connect failed: %s\n", ttx.what());
    return 1;
  }
  for (int test = 0; test < numTests; ++test) {
    sprintf(key_buf, "key%08d", test);
    string key(key_buf);
    std::string value;
    try {
      client.Get(value, key);
    } catch (EntityNotFoundException e) {
      //cout << e.message << endl;
      continue;
    }
  }
  transport->close();
  printf("Total Get after delete time: %lu us\n", now() - start);

  return 0;
}
